
#include "SchedulingAlgorithm_Greed.h"

using namespace ttg::devices;
using namespace ttg_internal::services;

//---------------------------------
//--- SchedulingAlgorithm_Greed ---
//---------------------------------

std::vector<DeviceID> SchedulingAlgorithm_Greed::createDevicePriorities(
	const std::vector<DeviceState *> &devices)
{
	std::vector<DeviceID> res;
	for (size_t i = 0; i < devices.size(); i++)
		if (devices[i]->getOwner()->getID() != CPU::getRef()->getID())
			res.push_back(devices[i]->getOwner()->getID());
	res.push_back(CPU::getRef()->getID());
	return res;
}

ttg::devices::LogicalDevice SchedulingAlgorithm_Greed::findSuitableApi(ttg::services::WiseTask *t,
																	   ttg::devices::DeviceID device)
{
	ttg::devices::LogicalDevice res;
	std::vector<ApiID> &api_id = t->getTaskKernels().getAPI(device);
	for (size_t i = 0; i < api_id.size(); i++)
	{
		res = LogicalDevice(device, api_id[i]);
		if (Device::getApi(res)->isAvailable())
			return res;
	}
	throw ttg::Error(ttg::Error::FATAL, "SchedulingAlgorithm_Greed", "findSuitableApi",
					 "Internal error - Cannot find corresponded API");
}

SchedulingAlgorithm_Greed::SchedulingAlgorithm_Greed(const std::vector<DeviceState *> &devices)
{
	//Registering devices.
	cpu_state = NULL;
	for (size_t i = 0; i < devices.size(); i++)
	{
		DeviceID id = devices[i]->getOwner()->getID();
		this->device_states[id] = devices[i];
		if (id == CPU::getRef()->getID())
			cpu_state = devices[i];
	}
	if (cpu_state == NULL)
		throw ttg::Error(ttg::Error::FATAL, "SchedulingAlgorithm_Greed", "SchedulingAlgorithm_Greed",
						 "CPU was not found!");
	//Creating priority list.
	device_priorities = createDevicePriorities(devices);
}

void SchedulingAlgorithm_Greed::split(std::vector<ttg::services::WiseTask *> &tasks,
									  std::vector<ttg::services::WiseTask *> &root_tasks,
									  std::vector<ttg::services::WiseTask *> &child_tasks)
{
	//Ignoring child tasks.
	root_tasks.insert(root_tasks.end(), tasks.begin(), tasks.end());
	tasks.clear();
}

void SchedulingAlgorithm_Greed::addTasks(ttg::structures::Enumerator<ttg::threads::Task *> &en)
{
	en.reset();
	while (en.moveNext())
		tasks.push(en.current());
}

void SchedulingAlgorithm_Greed::addTasks(ttg::structures::Enumerator<ttg::services::WiseTask *> &en)
{
	en.reset();
	while (en.moveNext())
	{
		ttg::services::WiseTask *t = en.current();
		std::vector<DeviceID> supported_devices = t->getTaskKernels().getDevices();
		for (size_t i = 0; i < supported_devices.size(); i++)
		{
			wise_tasks[supported_devices[i]].insert(t);
			device_states[supported_devices[i]]->getAvailableTaskCount() += 1;
		}
	}
}

void SchedulingAlgorithm_Greed::onPerformedTask(ttg::services::WiseTask *message)
{
	ttg::devices::DeviceID id = message->getSelectedDevice().getDevice();
	device_states[id]->getLocker().enter();
	device_states[id]->getActiveTaskCount() -= 1;
	if (id != CPU::x86::getRef()->getID())
	{
		cpu_state->getLocker().enter();
		cpu_state->getActiveTaskCount() -= 1;
		cpu_state->getLocker().leave();
	}
	device_states[id]->getLocker().leave();
}

ttg::threads::Task *SchedulingAlgorithm_Greed::getTask()
{
	//Trying to get simple task.
	if (tasks.size() != 0)
	{
		ttg::threads::Task *t = tasks.front();
		tasks.pop();
		return t;
	}
	//Trying to get wise task.
	ttg::services::WiseTask *t = NULL;
	for (size_t i = 0; i < device_priorities.size() && t == NULL; i++)
	{
		DeviceID id = device_priorities[i];
		DeviceState *state = device_states[id];
		state->getLocker().enter();
		if (state->getAvailableTaskCount() > 0 &&
			state->getActiveTaskCount() < state->getTotalTaskCount())
		{
			//Forming new task.
			state->getActiveTaskCount() += 1;
			if (id != CPU::getRef()->getID())
			{
				cpu_state->getLocker().enter();
				cpu_state->getActiveTaskCount() += 1;
				cpu_state->getLocker().leave();
			}
			t = *wise_tasks[id].rbegin();
			t->selectDevice(findSuitableApi(t, id));
			t->onFinish() += ttg::structures::make_delegate(this, &SchedulingAlgorithm_Greed::onPerformedTask);
			//Removing task.
			std::vector<DeviceID> devices = t->getTaskKernels().getDevices();
			for (size_t j = 0; j < devices.size(); j++)
			{
				wise_tasks[devices[j]].erase(t);
				device_states[devices[j]]->getAvailableTaskCount() -= 1;
			}
		}
		state->getLocker().leave();
	}
	return t;
}
